/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cp1300a2;
import java.util.Random;

/**
 *
 * @author nick wong
 */
public class Cars {

    private int h;// coordinate of a car
    private int v;// coordinate of a car
    private boolean moved;// weather a car has already moved in current iteration
    private int stats = NEW;// each car is given a stats during the process. can be : new / on raod / on intersection / finish
    private char type; //x or y of a car
    /**
     * max number of car can be genorrated per side
     */
    public static final int CAR_MAX = 12;
    /**
     * the lower the number the quicker car will enter one after another
     */
    public static final int CAR_ENTER_CHANCE = 5;

    //finial hence public
    //it needs to be call from other class
    /**
     * stats for cars location
     * new car has yet to enter road
     */
    public static final int NEW = 1;// new car has yet to enter road
    /**
     * stats for cars location
     * car has entered the road
     */
    public static final int ON_RAOD = 2;// car has entered road
    /**
     * stats for cars location
     * car has entered the intersection
     */
    public static final int ON_SECTION = 3;// car has entered intersection
    /**
     * stats for cars location
     * car has exited the road
     */
    public static final int FINISH = 9;// car has finish

    // car moving stats for moveCheck();
    /**
     * stats for cars move
     * if car is trying to move to a forward space is blocked such as another car is there
     */
    public static final int BLOCKED = 13; //
    /**
     * stats for cars move
     * car has successfuly moved forward to a new space
     */
    public static final int SUCCES = 11; //
    /**
     * stats for cars move
     * car is has to wait for green light
     */
    public static final int WAIT = 12; //

    private static int cars_count;// number of total cars

    private static Cars[] cars;// array of every car object


    /**
     * cars is a array of objects for every car
     *
     * each objects in car array contains:
     * int h - coordinate of a car
     * int v - coordinate of a car
     * boolean moved - weather a car has already moved in current iteration
     * int stats - each car is given a stats during the process. can be : new / on raod / on intersection / finish
     * char type - either x or y
     * 
     * @return return cars array
     */
    public static Cars[] getCars() {
        return cars;
    }

    /**
     * getter count how many cars
     * @return number of cars in int
     */
    public static int getCars_count() {
        return cars_count;
    }
    /**
     * setter count how many cars
     * @param total number of cars
     */
    public static void setCars_count(int cars_count) {
        Cars.cars_count = cars_count;
    }

    /**
     * contructor for cars
     * the following fields are created with the object:
     * int h - coordinate of a car - default is -1 for new car
     * int v - coordinate of a car - default is -1 for new car
     * boolean moved - weather a car has already moved in current iteration - default is false for new car
     * int stats - each car is given a stats during the process. default is NEW for new car
     * char type - can be x or y, x = horizontal | y = vertical - car_type must be passed as param - no error checking allowed
     *
     * @param car_type must be x or y, x = horizontal | y = vertical
     */
    public Cars(char car_type) {
        type = car_type;
        h = -1;
        v = -1;
        moved = false;
        stats = NEW;
    }
    //
    /**
     * Generate a random number
     *
     * A number must be pass as param so that it can be the maxiume number
     * for exsmaple: 88 is arg, it may gernerate a number between 0 to 88
     * 
     * @param num a number must be given as a maxisume number
     * @return generated number
     */
    public static int randGen(int num) {
        Random randomnum = new Random();
        int number = randomnum.nextInt(num);
        return number;
    }
    /**
     * Generate cars
     * 
     * using the
     *
     * @param car_type must be x or y, x = horizontal | y = vertical
     * @return a number of cars
     */
    public static int generateCar(char car_type) {
        int cars = 0;
        if (car_type == 'h') {
            cars = (Roads.getH_car() > 0) ? randGen(CAR_MAX): 0;
        } else if (car_type == 'v') {
            cars = (Roads.getV_car() > 0) ? randGen(CAR_MAX): 0;
        }
        return cars;
    }

    /**
     * check if there is car(s) on intersectoin
     *
     * @param type
     * @return return true if still car(s) there
     */
    public static boolean InIntersection(char type) {
        for (int i = 0; i < cars.length; i++) {
            if (cars[i].type == type && cars[i].stats == ON_SECTION) {
                return true;
            }
        }
        return false;
    }

    /**
     * check if x y is taken/blocked by another car
     *
     * loop throught all cars to check if same x y spot is taken/blocked by another car
     *
     * @param h the H location to check
     * @param v the V location to check
     * @param move_type must be x or y, x = horizontal | y = vertical
     * @return SUCCES if no car is using x y or return BLOCKED if is taken
     */
    public static int moveCheck(int h, int v, char move_type) {

        for (int i = 0; i < cars.length; i++) {// check car
            if (cars[i].h == h && cars[i].v == v ) {
                return BLOCKED;
            }

        }
        return SUCCES;

    }

    /**
     * handle car about to enter intersection
     *
     * before car is about to enter intersection we check if lights are green and no cars in opposite direction still in intersection by calling Cars.InIntersection
     *
     * @param h the H location to check
     * @param v the V location to check
     * @param move_type must be x or y, x = horizontal | y = vertical
     * @return return ON_SECTION if lights are green and no cars in opposite direction still in intersection
     *         return WAIT if lights are not green and/or there are still cars in opposite direction still in intersection
     */
    public static int moveCheckInterection(int h, int v, char move_type) {
        try {//grid_array[h][v] will throw ArrayIndexOutOfBoundsException if car is exiting raod
            // if car is entering a intersection we check if lights are green and no opsition car already in intereestion
                if (move_type == 'h' && Roads.getH_Lights() > Roads.LGITH_YELLOW && Cars.InIntersection('v') == false) {
                    System.out.println("=-=-=InIntersection H=-=-=-");
                    return ON_SECTION;
                } else if (move_type == 'v' && Roads.getV_Lights() > Roads.LGITH_YELLOW && Cars.InIntersection('h') == false) {
                    System.out.println("=-=-=InIntersection V=-=-=-");
                    return ON_SECTION;
                } else {
                    return WAIT;
                }
        } catch (ArrayIndexOutOfBoundsException e) {
            return FINISH;
        }

    }
    /**
     * update car object
     *
     * once all checking is complete we can assign new h v location to car object
     *
     * @param car_index the car we want to update
     * @param car_h the new H location to update
     * @param car_v the newe V location to update
     * @param stats stats of the car
     */
    public static void moveUpdate(int car_index, int car_h, int car_v, int stats) {
        //moveDebug(car_index);
        int i = car_index;
        cars[i].h = car_h;
        cars[i].v = car_v;
        cars[i].moved = true;
        cars[i].stats = stats;
        //moveDebug(car_index);
    }
    /**
     * cars debug function
     *
     * output all cars object
     */
    public static void moveDebug() {
        System.out.println("=-=-=Debug All Cars=-=-=-");
        for (int i = 0; i < cars.length; i++) {
            System.out.println("==Car#:" + i +"==");
            System.out.println("H:" + cars[i].h);
            System.out.println("V:" + cars[i].v);
            System.out.println("Moved:" + cars[i].moved);
            System.out.println("Type:" + cars[i].type);
            System.out.println("Stats:" + cars[i].stats);
        }
        System.out.println("=-=-=DEBUG=-=-=-");
    }
    /**
     * car debug function
     *
     * output car object
     *
     * @param i array index of the car we want to debug
     */
    public static void moveDebug(int i) {
        System.out.println("=-=-=Debug Car# "+ i +" =-=-=-");
        System.out.println("Car#:" + i);
        System.out.println("H:" + cars[i].h);
        System.out.println("V:" + cars[i].v);
        System.out.println("Moved:" + cars[i].moved);
        System.out.println("Type:" + cars[i].type);
        System.out.println("Stats:" + cars[i].stats);
        System.out.println("=-=-=DEBUG=-=-=-");
    }
    /**
     * reset all cars
     *
     * reset all cars so cars can move again the next loop.
     * usually called at the end of each loop
     */
    public static void moveReset() {
        for (int i = 0; i < cars.length; i++) {
            cars[i].moved = false;
        }
    }
    /**
     * check x y for car
     *
     * check all cars to see if a car is at h v location
     *
     * @param grid_h h location we want to check
     * @param grid_v v location we want to check
     * @return return true if car is at given h v
     */
    public static boolean moveDisplay(int grid_h, int grid_v) {
        for (int i = 0; i < cars.length; i++) {
            if (cars[i].h == grid_h && cars[i].v == grid_v) {
                return true;
            }
        }
        return false;
    }
    /**
     * car move function
     *
     * this function checks a given grid position a car
     * and use appropriate action if a car is found on the given location
     *
     *
     * @param grid_type must be x or y, x = horizontal | y = vertical
     * @param grid_h H location to check
     * @param grid_v V location to check
     * @param road_type possible value: ON_RAOD or ON_SECTION - caller need to provide this to so we know if the location is on raod or on section
     * @return empty space ' ' is returned if no car is at given location. If given location has a car and all condition are pass it will return 'C' which represent a car.
     *
     */
    public static char move(char grid_type, int grid_h, int grid_v, int road_type) {
        char[][] grid_array = Roads.getGrid_array();// need to bring grid_array into scope to check grid_array

        //System.out.println(road_type);
        //System.out.println("=-=-=road_type=-=-=-");

        for (int i = 0; i < cars.length; i++) {
            if (cars[i].moved == true || cars[i].stats == FINISH) {// skip to next car if current car has moved
                continue;
            }
            if ((grid_v == 0 && cars[i].type == 'v' || grid_h == 0 && cars[i].type == 'h') && cars[i].h == -1 && cars[i].v == -1) {// new car entering
                if (moveCheck(grid_h, grid_v, grid_type) == SUCCES) {//
                    if (randGen(CAR_ENTER_CHANCE) == 0) {// do we want car enter randomly or one after another?
                        moveUpdate(i, grid_h, grid_v, ON_RAOD);
                    } else {
                        moveUpdate(i, cars[i].h, cars[i].v, NEW);// keep car as NEW so it can enter next loop
                    }
                }
            } else if (cars[i].h == grid_h && cars[i].v == grid_v) {
                int move = 0;
                if(grid_type == cars[i].type && road_type == Cars.ON_SECTION && cars[i].stats == Cars.ON_SECTION) {// catch cars in section

                    //move = ON_SECTION;
                    
                    if (grid_type == 'h' ) {
                        //move = ON_SECTION;
                        moveUpdate(i, grid_h + 1, grid_v, road_type);
                    } else if (grid_type == 'v') {
                        //move = ON_SECTION;
                        moveUpdate(i, grid_h, grid_v + 1, road_type);
                    }
                //} else if (road_type == cars[i].ON_RAOD && grid_array[grid_h][grid_v] == Roads.ROAD_SECTION) {// catch cars above to enter section
                    
                } else {// cars on road and on intersection
                    try {
                        if (cars[i].type == 'v' && grid_type == 'v' ) {
                            if (cars[i].stats == ON_RAOD && grid_array[grid_h][grid_v + 1] == Roads.ROAD_SECTION) {
                                //System.exit(1);
                                move = moveCheckInterection(grid_h, grid_v + 1, grid_type);
                            } else {
                                move = moveCheck(grid_h, grid_v + 1, grid_type);
                            }
                        }
                        if (cars[i].type == 'h' && grid_type == 'h') {
                            if (cars[i].stats == ON_RAOD && grid_array[grid_h + 1][grid_v] == Roads.ROAD_SECTION) {
                                //System.exit(1);
                                move = moveCheckInterection(grid_h + 1, grid_v, grid_type);
                            } else {
                                move = moveCheck(grid_h + 1, grid_v, grid_type);
                            }
                        }
                    } catch (ArrayIndexOutOfBoundsException e) {
                        move = FINISH;// if out of array means cars has exited raod
                    }
                    if (move > 0) {
                        System.out.println("=-=-= "+  move +" =-=-=-");
                        switch (move) {
                            case ON_SECTION://handle new car just about to enter interesct
                                road_type = ON_SECTION;
                            case SUCCES://handle car that can move success
                                if (cars[i].type == 'v') {
                                    moveUpdate(i, grid_h, grid_v + 1, road_type);
                                } else if (cars[i].type == 'h') {
                                    moveUpdate(i, grid_h + 1, grid_v, road_type);
                                }
                                //System.out.println("=-=-=SUCCES=-=-=-");
                                break;
                            case FINISH:// handle car exited raod
                                moveUpdate(i, 0, 0, FINISH);
                                Cars.setCars_count(Cars.getCars_count() - 1);
                                //System.out.println("=-=-=FINISH=-=-=-");
                                break;
                            case BLOCKED:
                            case WAIT:// handle new car about to enter raod
                            case NEW:// handle new car entered raod
                                //System.out.println("=-=-=WAITED=-=-=-");
                                moveUpdate(i, cars[i].h, cars[i].v, road_type);

                        }
                    } else {
                        System.out.println("No car");//debug
                        //System.exit(1);
                    }
                }
            }

        }
        return ' ';
    }

    /**
     * generate car objects
     *
     * generate both h and v car objects at startup
     *
     *
     */
    public static void init() {
        int h_car = generateCar('h');
        int v_car = generateCar('v');

        int cars_total = cars_count = h_car + v_car;

        cars = new Cars[cars_total];
        //2 while loop to create h and v cars
        while (cars_total > 0) {
            while (h_car > 0) {
                cars[cars_total - 1] = new Cars('h');
                cars_total--;
                h_car--;
            }
            while (v_car > 0) {
                cars[cars_total - 1] = new Cars('v');
                cars_total--;
                v_car--;
            }
        }
    }
    /**
     * count number of cars traveling
     *
     * count how many cars are yet to exit road(still traveling) and return the number of car(s)
     *
     * @return the number of cars traveling
     */
    public static int traveling() {
        int teveling = 0;
        for (int i = 0; i < cars.length; i++) {
            if (cars[i].stats != FINISH) {
                teveling++;
            }
        }
        return teveling;
    }
}
